package util.estatisticas;

import java.util.ArrayList;

import util.estatisticas.metricas.NumeroMedioUsuarios;
import util.estatisticas.metricas.TempoMedioDownload;
import util.estatisticas.metricas.TrocasMalSucedidas;
import util.estatisticas.metricas.Vazao;
import util.output.Saida;
import entidades.Peer;
import entidades.Seed;

public class Estatistica {
	
	private ArrayList<Peer> listaPeer;
	
	private ArrayList<Seed> listaSeed;
	
	private TempoMedioDownload tempoMedioDownload;
	
	private NumeroMedioUsuarios numeroMedioUsuarios;
	
	private Vazao vazao;
	
	private TrocasMalSucedidas trocasMalSucedidas;

	private int rodada;

	/**
	 * Construtor
	 * 
	 * @param rodadaFaseTransiente
	 * @param numeroUsuariosIniciaisRodada
	 */
	public Estatistica(int rodadaFaseTransiente, int numeroUsuariosIniciaisRodada) {
		this.listaPeer = new ArrayList<Peer>();
		this.listaSeed = new ArrayList<Seed>();
		this.tempoMedioDownload = new TempoMedioDownload();
		this.numeroMedioUsuarios = new NumeroMedioUsuarios();
		this.vazao = new Vazao();
		this.trocasMalSucedidas = new TrocasMalSucedidas();
		this.rodada = rodadaFaseTransiente;
	}
	
	/**
	 * Método a ser chamado sempre que houver uma chegada de um usuário do sistema.
	 * Ao final de uma rodada, essa lista de peers deverá ser reiniciada.
	 * 
	 * @param peer
	 */
	public void chegadaUsuario(Peer peer) {
		this.listaPeer.add(peer);
		this.numeroMedioUsuarios.chegadaUsuario(peer.getInstanteChegada());
	}

	/**
	 * Método a ser chamado sempre que houver uma saída de um usuário do sistema.
	 * Ao final de uma rodada, essa lista de seeds deverá ser reiniciada.
	 * 
	 * @param seed
	 */
	public void saidaUsuario(Seed seed) {
		this.listaSeed.add(seed);
		this.numeroMedioUsuarios.saidaUsuario(seed.getInstanteSaida());
		this.vazao.saidaUsuario();
	}
	
	/**
	 * Finaliza a simulação.
	 * 
	 */
	public void finalizaSimulacao() {
		this.tempoMedioDownload.plot();
		this.tempoMedioDownload.imprimeIC();
		this.tempoMedioDownload.imprimeMediana();
		this.numeroMedioUsuarios.plotPMF();
		this.trocasMalSucedidas.plot();
		this.vazao.escreveArquivo();
	}

	/**
	 * Finaliza a rodada.
	 * 
	 */
	public void finalizaRodada(double instanteFimRodada) {
		this.numeroMedioUsuarios.calculaXi();
		this.tempoMedioDownload.calculaXi(this.rodada, this.listaPeer);
		this.trocasMalSucedidas.calculaXi(this.rodada, this.listaPeer);
		this.vazao.finalizaRodada(instanteFimRodada);
		
		this.listaPeer.clear();
		this.listaSeed.clear();
	}

	/**
	 * Inicializa a rodada.
	 * 
	 * @param rodada
	 * @param instanteInicioRodada 
	 * @param numeroUsuariosIniciaisRodada
	 */
	public void inicializaRodada(int rodada, double instanteInicioRodada, int numeroUsuariosIniciaisRodada) {
		this.rodada = rodada;
		Saida.imprime("Instante de início da rodada " + rodada + ": " + instanteInicioRodada);
		
		this.numeroMedioUsuarios.setRodada(rodada);
		this.numeroMedioUsuarios.setInstanteInicioRodada(instanteInicioRodada);
		this.numeroMedioUsuarios.setNumeroUsuariosIniciaisRodada(numeroUsuariosIniciaisRodada);
		this.vazao.inicializaRodada(instanteInicioRodada);
	}

	/**
	 * Vefifica se o intervalo de de confiança do tempo de download atingiu 95%
	 * 
	 * @return Se o intervalo de confiança do tempo de download atingiu 95%
	 */
	public boolean isIntervaloConfiancaSatisfeito() {
		double ladoEsquerdo = 2*1.96*Math.sqrt(this.tempoMedioDownload.getEstimadorVariancia()) / Math.sqrt(this.rodada);
		double ladoDireito = 0.1*this.tempoMedioDownload.getEstimadorMedia();
		
		return ladoEsquerdo < ladoDireito;
	}
}
